/**
 * Copyright (C) 2007 Mustafa K. Isik - isik@acm.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

package com.paxcodi.threads.diner;

import java.util.ArrayList;
import java.util.List;

class CircularTable {

	private static final CircularTable INSTANCE = new CircularTable();

	private List<Chopstick> chopsticks; //indices represent seatNumbers

	private int numberOfPhilosophers;

	/**
	 * Private constructor ensures non-instantiability of the <code>CircularTable</code> class via external calls.
	 */
	private CircularTable(){
		chopsticks = new ArrayList<Chopstick>();
	}

	public void setNumberOfPhilosophers(int numberOfPhilosophers){
		this.numberOfPhilosophers = numberOfPhilosophers;
	}

	/**
	 * Enforces singleton property of the <code>CircularTable</code> class.
	 * @return only instance of this class
	 */
	public static CircularTable getInstance(){
		return INSTANCE;
	}

	private void serveTable(){
		//sticks need to be on the table before any philosopher is seated
		//that is why the two following loops should not be replaced by a single one
		for(int j = 0; j < numberOfPhilosophers; j++){
			chopsticks.add(new Chopstick(j)); //indexpos of sticks is of prime importance
		}

	}

	private void seatPhilosophers(){
		//seat philosophers at table and make them aware of their respective seat number
		//which is important to identify the relevant chopstick indices
		for(int i = 0; i < numberOfPhilosophers; i++){
			(new Thread(new Philosopher(i), Integer.toString(i))).start();
		}
	}

	synchronized public boolean requestChopsticksForSeat(int requestingSeatIndex){
		Chopstick leftStick = chopsticks.get(requestingSeatIndex);
		Chopstick rightStick = chopsticks.get((requestingSeatIndex + 1) % numberOfPhilosophers);
		while(leftStick.isBeingUsed() || rightStick.isBeingUsed()){
			System.out.println("P " + Thread.currentThread().getName() + " is waiting for sticks.");
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("P " + Thread.currentThread().getName() + " picks up sticks " + 
				leftStick.getIndex() + " and " + rightStick.getIndex());
		leftStick.setInUse(true);
		rightStick.setInUse(true);
		System.out.println("Sticks " + leftStick.getIndex() + " and " + 
				rightStick.getIndex() + " are in use by P " + 
				Thread.currentThread().getName());
		return true;
	}

	synchronized public void releaseChopsticksForSeat(int releasingSeatIndex){
		Chopstick leftStick = chopsticks.get(releasingSeatIndex);
		Chopstick rightStick = chopsticks.get((releasingSeatIndex + 1) % numberOfPhilosophers);
		System.out.println("Philosopher number " + Thread.currentThread().getName() + " puts down chopsticks.");
		leftStick.setInUse(false);
		rightStick.setInUse(false);

		notifyAll();
	}


	/**
	 * 
	 * @param args first commandline parameter indicates number of philosophers
	 */
	public static void main(String args[]){

		int numberOfPhilosophers = Integer.parseInt(args[0]);

		CircularTable.getInstance().setNumberOfPhilosophers(numberOfPhilosophers);

		CircularTable.getInstance().serveTable();

		CircularTable.getInstance().seatPhilosophers();

	}

}
